/**
 * @file cdnet_core.c
 * @brief CDNET核心协议实现 - Arduino库扩展
 * @details 增强的CDNET协议实现，提供多接口支持和高级路由功能
 *          包含命名空间管理、套接字管理、路由选择等核心功能
 * 
 * @author 原作者：Duke Fong <d@d-l.io>
 * @author 修改团队：mastwet@Undefine Innovation
 * @date 2024
 * @copyright MIT License
 * @note 基于Duke Fong的cdnet核心协议实现
 */

#include "cdnet_core.h"
#include "cdnet.h"

/**
 * @brief 路由选择函数
 * @param ns CDNET命名空间指针
 * @param pkt CDNET数据包指针
 * @return 网络接口指针，失败返回NULL
 * @details 根据数据包的目标地址选择最合适的网络接口
 *          在Arduino实现中，使用单接口模式
 */
cdn_intf_t *cdn_route(cdn_ns_t *ns, cdn_pkt_t *pkt)
{
    // Arduino实现中使用单接口
    if (CDN_INTF_MAX > 0) {
        return &ns->intfs[0];
    }
    return NULL;
}

/**
 * @brief 通过网络接口发送帧数据
 * @param ns CDNET命名空间指针
 * @param pkt CDNET数据包指针
 * @return 发送结果状态码
 * @details 将数据包通过选定的网络接口发送出去
 *          包括路由选择、帧准备、设备发送等步骤
 */
int cdn_send_frame(cdn_ns_t *ns, cdn_pkt_t *pkt)
{
    cdn_intf_t *intf = cdn_route(ns, pkt);
    if (!intf) {
        return CDN_RET_ROUTE_ERR;
    }
    
    // Arduino实现中委托给底层CDBUS设备
    cd_dev_t *dev = (cd_dev_t *)intf->dev;
    if (!dev)
        return CDN_RET_ROUTE_ERR;
    
    // 准备传输帧
    int ret = cdn_frame_w(pkt);
    if (ret < 0)
        return ret;
    
    // 发送帧数据
    dev->put_tx_frame(dev, pkt->frm);
    pkt->frm = NULL; // 帧所有权已转移
    
    return 0;
}

/**
 * @brief 通过网络发送数据包
 * @param ns CDNET命名空间指针
 * @param pkt CDNET数据包指针
 * @return 发送结果状态码
 * @details 将数据包通过选定的网络接口发送出去
 */
int cdn_send_pkt(cdn_ns_t *ns, cdn_pkt_t *pkt)
{
    return cdn_send_frame(ns, pkt);
}

/**
 * @brief 根据端口号查找套接字
 * @param ns CDNET命名空间指针
 * @param port 端口号
 * @return 套接字指针，未找到返回NULL
 * @details 在命名空间的套接字列表中查找指定端口号的套接字
 */
static cdn_sock_t *cdn_sock_search(cdn_ns_t *ns, uint16_t port)
{
    list_node_t *node;
    list_for_each_ro(&ns->socks, node) {
        cdn_sock_t *sock = list_entry(node, cdn_sock_t);
        if (sock->port == port) {
            return sock;
        }
    }
    return NULL;
}

/**
 * @brief 将套接字绑定到端口
 * @param sock 套接字指针
 * @return 绑定结果：0成功，-1端口已被占用
 * @details 将套接字绑定到指定端口，端口不能重复绑定
 */
int cdn_sock_bind(cdn_sock_t *sock)
{
    if (cdn_sock_search(sock->ns, sock->port))
        return -1; // 端口已被占用
    
    list_put(&sock->ns->socks, &sock->node);
    return 0;
}

/**
 * @brief 通过套接字发送数据包
 * @param sock 套接字指针
 * @param pkt CDNET数据包指针
 * @return 发送结果状态码
 * @details 将数据包通过指定套接字发送出去
 */
int cdn_sock_sendto(cdn_sock_t *sock, cdn_pkt_t *pkt)
{
    if (sock->port == 0)
        return -1; // 套接字未绑定
    
    cdn_pkt_prepare(sock, pkt);
    return cdn_send_pkt(sock->ns, pkt);
}

/**
 * @brief 从套接字接收数据包
 * @param sock 套接字指针
 * @return 数据包指针，无数据返回NULL
 * @details 从套接字的接收队列中获取数据包
 */
cdn_pkt_t *cdn_sock_recvfrom(cdn_sock_t *sock)
{
    list_node_t *node = list_get(&sock->rx_head);
    if (node)
        return list_entry(node, cdn_pkt_t);
    return NULL;
}

/**
 * @brief 处理接收到的数据包
 * @param ns CDNET命名空间指针
 * @details 处理所有网络接口接收到的数据包
 *          包括帧解析、套接字匹配、数据包分发等步骤
 */
void cdn_routine(cdn_ns_t *ns)
{
    cdn_intf_t *intf;
    cdn_pkt_t *pkt;
    cdn_sock_t *sock;
    list_node_t *node;
    
    // 处理每个接口
    for (int i = 0; i < CDN_INTF_MAX; i++) {
        intf = &ns->intfs[i];
        if (!intf->dev) continue;
        
        cd_dev_t *dev = (cd_dev_t *)intf->dev;
        
        // 获取接收到的帧
        while ((pkt = (cdn_pkt_t *)dev->get_rx_frame(dev)) != NULL) {
            // 解析帧数据
            if (cdn_frame_r(pkt) < 0) {
                cdn_pkt_free(ns, pkt);
                continue;
            }
            
            // 查找匹配的套接字
            bool delivered = false;
            list_for_each_ro(&ns->socks, node) {
                sock = list_entry(node, cdn_sock_t);
                if (sock->port == pkt->dst.port && !sock->tx_only) {
                    // 分发到套接字
                    list_put(&sock->rx_head, pkt);
                    delivered = true;
                    break;
                }
            }
            
            if (!delivered) {
                cdn_pkt_free(ns, pkt);
            }
        }
    }
}

/**
 * @brief 初始化命名空间
 * @param ns CDNET命名空间指针
 * @param free_pkt 空闲数据包链表头
 * @param free_frm 空闲帧链表头
 * @details 初始化CDNET命名空间，包括套接字列表和接口数组
 */
void cdn_init_ns(cdn_ns_t *ns, list_head_t *free_pkt, list_head_t *free_frm)
{
    memset(ns, 0, sizeof(cdn_ns_t));
    ns->free_pkt = free_pkt;
    ns->free_frm = free_frm;
    list_head_init(&ns->socks);
    
    for (int i = 0; i < CDN_INTF_MAX; i++) {
        ns->intfs[i].dev = NULL;
        ns->intfs[i].net = 0;
        ns->intfs[i].mac = 0;
    }
}

/**
 * @brief 添加网络接口到命名空间
 * @param ns CDNET命名空间指针
 * @param dev 设备指针
 * @param net 网络号
 * @param mac MAC地址
 * @return 添加结果：0成功，-1无空闲接口槽位
 * @details 将网络设备添加到命名空间的接口数组中
 */
int cdn_add_intf(cdn_ns_t *ns, void *dev, uint8_t net, uint8_t mac)
{
    for (int i = 0; i < CDN_INTF_MAX; i++) {
        if (ns->intfs[i].dev == NULL) {
            ns->intfs[i].dev = dev;
            ns->intfs[i].net = net;
            ns->intfs[i].mac = mac;
            return 0;
        }
    }
    return -1; // 无空闲接口槽位
}